#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <time.h>
#include <syslog.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include "server.h"
#include "../clsv/clsv.h"
#include "../clsv/ipc/sig.h"
#include "../clsv/log.h"
#include "listADT.h"

#define	HOST		"localhost"
#define	PORT		"4500"

#define TIMEOUT   4

#define	FILE_BUFFER	MAX_DATA


extern counter;
extern int errno;
extern int useLog;
TsrvData srvData;

static pid_t servpid;
extern int errno;

typedef struct sessionInfo {
	int session;
	int pid; 			/* PID of the child process that handles the session */
} sessionInfoT;

typedef struct cliInfo {
	int upid;
	char nick[MAX_NICK+1];
} cliInfoT;

typedef struct Tmsg
{
	long mtype; 			/* Message type */
	char mtext[MSG_MAX]; 	/* Message text */
} Tmsg;

struct server_info {
	int mqid;
	int mqid2;
	int cantelem;
	int spid;
	int cpid;
	int session;
	listADT list;
};

static void (*proc[])(CLSV_BUFFER * p) =
{
	process_conn,
	process_conn,
	process_lists,
	process_listu,
	process_disc,
	process_chMessR,
	process_chMessS,
	process_info,
};

static void (*proch[])(CLSV_BUFFER * p) =
{
	process_chConn,
	process_chConn,
	process_chLists,
	process_chListu,
	process_chDisc,
	process_chMessR,
	process_chMessS,
};

struct server_info _info;

int myMsgsnd(Tmsg * msg, int time, int mqid);

int myMsgrcv(Tmsg * msg, int time, int pid, int mqid);

extern int alrm;


int
myMsgsnd(Tmsg *msg, int timeout, int mqid)
{
	int ans = -1;

	if(timeout)
		setAlarm(timeout, nothing);
	//printf("myMsgsnd: message= %d\t queue= %d in: %d\n", msg->mtype, mqid, getpid());
	ans = msgsnd(mqid, msg, sizeof(Tmsg), 0);

	if(timeout)
		unsetAlarm();

	if( ans == -1 && errno != EINTR)
	{
		if( errno == EACCES )
			perror("Error: Sending msg queue,acces denied, check queue "
					"permissions");
		else
			perror("Error: Sending, msg queue, unexpected error");

		return ans;
	}
	return (errno == EINTR ? 1 : !ans);
}

int
myMsgrcv(Tmsg * msg, int timeout, int pid, int mqid)
{
	int ans;


	if(timeout)
		setAlarm(timeout, nothing);

	ans = msgrcv(mqid, msg, sizeof(Tmsg), pid, MSG_NOERROR);
	//printf("myMsgrcv: message= %d\t in: %d\n", msg->mtype, getpid());
	if(timeout)
		unsetAlarm();
	if( ans == -1 && errno != EINTR)
	{
		if( errno == EACCES )
			perror("Error: Receiving msg queue,acces denied, check queue "
					"permissions");
		else
			perror("Error: Receiving, msg queue, unexpected error");
	}
	return (errno == EINTR ? 1 : ans);

}

int
compare( listElementT elem1, listElementT elem2)
{
	if(((sessionInfoT *)elem1)->session > ((sessionInfoT *)elem2)->session)
		return 1;
	else if(((sessionInfoT *)elem1)->session == ((sessionInfoT *)elem2)->session)
		return 0;
	else
		return -1;
}

listElementT
copy(listElementT  elem)
{
	sessionInfoT * ret, * aux;

	if((ret = malloc(sizeof(sessionInfoT))) == NULL)
	{
		myError(ERROR_MALLOC);
		return NULL;
	}
	aux = (sessionInfoT *) elem;
	ret->session = aux->session;
	ret->pid = aux->pid;

	return ret;
}

void
freeNode(listElementT  elem)
{
	free((sessionInfoT *)elem);
}



void
waiter(int s)
{
	int status;

	while (wait3(&status, WNOHANG, (struct rusage *)0) > 0);
	return;
}



static int
createSessionServer(void)
{
	int pid;

	switch ( pid = fork() )
	{
		case -1:
			fatal("Error forking");
			break;

		case 0:
			doSessionServer();
			break;
	}
	return pid;
}

void
doSessionServer(void)
{
	int msqid, n;
	listADT list;
	sessionInfoT * element;
	Tmsg message;
	CLSV_BUFFER p;

	/*creating internal server queues*/
	if((_info.mqid = msgget(MSG_KEY, QPERM | IPC_CREAT | IPC_EXCL)) == -1)
	{
		if( errno == EEXIST )
			fatal("Error: Message queue already exists");
		else
			fatal("Error: Can't create queue, unexpected error");
	}

	if((_info.mqid2 = msgget(MSG_KEY2, QPERM | IPC_CREAT | IPC_EXCL)) == -1)
	{
		if( errno == EEXIST )
			fatal("Error: Message queue already exists");
		else
			fatal("Error: Can't create queue, unexpected error");
	}

	/*creating serssion servers list*/
	if((_info.list = listNew(compare, copy, freeNode)) == NULL)
	{
		fprintf(stderr, "Can't create new list for server information.\n");
		exit(EXIT_FAILURE);
	}

	forever
	{
		/*receiving message from child process*/
		if(myMsgrcv(&message, 0, 1, _info.mqid) != -1)
		{
			if(errno!=EINTR)
			{
				memcpy(&p, message.mtext, sizeof(CLSV_BUFFER));
				if(p.opcode > 0 && p.opcode < MAX_OPCODE)
					(*proc[p.opcode])(&p);
				else
					fprintf(stderr, "Incorrect opcode. Received %d\n",p.opcode);
			}
		}
	}
}

int
main(int argc, char *argv[])
{
	int mqid, pid;
	int error;

	mqid = msgget(0x22, 0);
	msgctl(mqid, IPC_RMID, NULL);
	mqid = msgget(0x22 + 10, 0);
	msgctl(mqid, IPC_RMID, NULL);

	mqid = msgget(MSG_KEY, 0);
	msgctl(mqid, IPC_RMID, NULL);
	mqid = msgget(MSG_KEY2, 0);
	msgctl(mqid, IPC_RMID, NULL);
	
	/* Server data init */
	srvData.cliSess = NULL;
	srvData.srvSess = NULL;
	_info.spid = -1;
	_info.session = -1;

	/* Log initialize */
	useLog = LOG_YES;
	logOpen("Chat Server.");

	/* Get the configuration parameters */
	if( getConf(argc, argv, &srvData) )
	{
		logEvent("Invalid parameters.");
		return ERROR_PARAMS;
	}

	/* Check for other instances */
	error = checkInstance(&srvData);
	switch( ERROR_OK )
	{
		case ERROR_OK:
			logEvent("Instance OK.");
			break;
		case ERROR_INST:
			logEvent("Another instance is running.");
			exit(1);
			break;
		default:
			logEvent("Instance initialization failure.");
			exit(1);
			break;
	}


	signal(SIGCHLD, waiter);

	/*creating the server to manage the session list */
	pid= createSessionServer();

	/* Server initialization */
	if (initSessServer(srvData.ipc, srvData.port, &(srvData.srvSess))
								!= ERROR_OK )
	{
		logEvent("Session initialization failed.");
		return 1;
	}

	/* Close server on SIGHUP, SIGTERM, etc */
	intrHandler();

	/* Server */
	forever
		server(&srvData);

	/* Server shutdown */
	exitServer(0);

	return ERROR_OK;
}


/*--------------------------------------------------------------------------*/

int
getConf(int argc, char ** argv, TsrvData *srvData)
{
	int index= 1;
	static Tfifo fifo;
	static Tshm shm;
	static int key;

	/* Usage: server IPC PORT */
	if( argc != 2 )
		return ERROR_DEFAULT;

	if( strcmp(argv[index], USE_FIFO_STR) == 0 )
	{
		system("rm /tmp/chat*");
		srvData->ipc = SESS_FIFO;
		srvData->instance = INSTANCE_KEY_FIFO;
		fifo.write = KEY_FIFO_SND;
		fifo.read = KEY_FIFO_RCV;
		srvData->port = &fifo;
	}
	else if( strcmp(argv[index], USE_TCP_STR) == 0 )
	{
		srvData->ipc = SESS_TCP;
		srvData->instance = INSTANCE_KEY_TCP;
		srvData->port = KEY_TCP;
	}
	else if( strcmp(argv[index], USE_SHM_STR) == 0 )
	{
		srvData->ipc = SESS_SHM;
		srvData->instance = INSTANCE_KEY_SHM;
		shm.keySnd = KEY_SHM_SND;
		shm.keyRcv = KEY_SHM_RCV;
		srvData->port = &shm;
	}
	else if( strcmp(argv[index], USE_MSG_STR) == 0 )
	{
		srvData->ipc = SESS_MSG;
		srvData->instance = INSTANCE_KEY_MSG;
		key = KEY_MSG;
		srvData->port = &key;
	}
	else
		return ERROR_PARAMS;

	return ERROR_OK;
}

int
checkInstance(TsrvData *srvData)
{
	int ans;

	if( (ans= openSem(srvData->instance,1)) == ERROR_EXIST )
		ans= createSem(srvData->instance, 1, INSTANCE_PERMS);

	if( ans >= 0 )
		return pInstanceSem(ans, 0);
	else
		return ans;

}

int
intrHandler(void)
{
	struct sigaction act;

	/* Block other signals */
	if( sigfillset(&(act.sa_mask)) == -1)
		return ERROR_DEFAULT;
	if( sigdelset(&(act.sa_mask),SIGALRM) == -1)
		return ERROR_DEFAULT;

	/* Close server on exit */
	act.sa_handler = exitServer;

	if( sigaction(SIGINT, &act, NULL) == -1)
		return ERROR_DEFAULT;

	if( sigaction(SIGSEGV, &act, NULL) == -1)
		return ERROR_DEFAULT;
	return ERROR_OK;
}

void
exitServer(int s)
{
	if( srvData.cliSess != NULL )
		closeSess(srvData.cliSess);
	if( srvData.srvSess != NULL )
		closeSess(srvData.srvSess);

	srvData.cliSess= NULL;
	srvData.srvSess= NULL;
	logClose();
	exit(ERROR_UNEX);
}

int
server(TsrvData * srvData)
{
	int pid;

	counter++;
	if( listenSess(srvData->srvSess, &(srvData->cliSess)) != ERROR_OK )
	{
//		logEvent("Error listening.\n");
		return 1;
	}
	//logEvent("\nNew Client!\n");

	if( ( pid = fork() ) == -1 )
	{
		logEvent("Error forking child process.\n");
		closeSess(srvData->srvSess);
		closeSess(srvData->cliSess);
		srvData->srvSess= NULL;
		srvData->cliSess= NULL;
		exit(ERROR_UNEX);
	}

	if( pid == 0 )		/* Child Process */
	{
		if( srvData->ipc == SESS_TCP )
		{
			closeSess(srvData->srvSess);
			srvData->srvSess= NULL;
		}

		processCli(srvData->cliSess);

		if( closeSess(srvData->cliSess) != ERROR_OK )
			logEvent("Error closing session.\n");

		srvData->cliSess= NULL;
		exit(0);
	}

	if( srvData->ipc == SESS_TCP )
		closeSess(srvData->cliSess);

	srvData->cliSess= NULL;
	return 0;

}


int
processCli(sessADT sess)
{
	Tmsg msg;
	CLSV_BUFFER buffer;
	int pid, error;

	if((_info.mqid = msgget(MSG_KEY, 0)) == -1)
	{
		perror("Error: Can't access message queue");
		return;
	}

	pid = getpid();

	do{
		
		errno = 0;
		memset(&buffer, 0, sizeof(CLSV_BUFFER));

		error= getMsg(sess, &buffer, sizeof(CLSV_BUFFER), 1);

		if(errno != EINTR && error)
		{
			if(buffer.opcode > 0 && buffer.opcode <= MAX_OPCODE)
				proch[buffer.opcode](&buffer);
		}
		
		errno = 0;
		memset(&msg, 0, sizeof(Tmsg));
		myMsgrcv(&msg, TIMEOUT, pid, _info.mqid);

		if(errno != EINTR && msg.mtype == pid)
		{
			
			memcpy(&buffer, msg.mtext, sizeof(CLSV_BUFFER));
			if(buffer.opcode > 0 && buffer.opcode <= MAX_OPCODE)
			proch[buffer.opcode](&buffer);
		}
		
		errno = 0;
		memset(&msg, 0, sizeof(Tmsg));
		myMsgrcv(&msg, TIMEOUT, pid, _info.mqid2);
		if(errno != EINTR && msg.mtype == pid)
		{
			memcpy(&buffer, msg.mtext, sizeof(CLSV_BUFFER));
			if(buffer.opcode == CHATS)
				process_chMessS(&buffer);
			if(buffer.opcode == CHATR)
				process_chMessR(&buffer);
		}

	} while(_info.session > 0);




	return ERROR_OK;
}


/*----------------------------------------------------------------------------*/


void
process_conn(CLSV_BUFFER * data)
{
	Tmsg msg;
	CLSV_BUFFER buffer;
	sessionInfoT session, *answer;

	/* searching for the session in the sessions list*/
	session.session = data->data.clsv_conn.session;

	if((answer = GetDatoComp(_info.list, &session)) == NULL)
	{/* if the session doesn't exist*/
		buffer.opcode = CONN;
		msg.mtype = data->data.clsv_conn.upid;
		buffer.data.svcl_conn.status = NO_SERVER;
		buffer.data.svcl_conn.spid = -1;
		memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
	}
	else
	{
		msg.mtype = answer->pid;
		memcpy(msg.mtext, data, sizeof(CLSV_BUFFER));
	}

	myMsgsnd(&msg, TIMEOUT, _info.mqid);

	return;
}


void
process_lists(CLSV_BUFFER * data)
{
	Tmsg msg;
	CLSV_BUFFER buffer;
	sessionInfoT * session;
	int i=0;
	if(data != NULL && _info.list != NULL)
	{
		ToBegin(_info.list);

		while((session = GetDato(_info.list)) != NULL)
		{
			i += sprintf((buffer.data.svcl_lists.list)+i, "%d\n", session->session);
		}
		(buffer.data.svcl_lists.list)[i] = 0;
		if(i == 0)
			buffer.data.svcl_lists.status = ERROR_NO_SESSION;
		else
			buffer.data.svcl_lists.status = OK_;

	}
	else
	{
		buffer.data.svcl_lists.status = ERROR_LISTS;
		return;
	}
	buffer.opcode = LISTS;
	msg.mtype = data->data.clsv_lists.upid;
	memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
	myMsgsnd(&msg, TIMEOUT, _info.mqid);

	return;
}


void
process_listu(CLSV_BUFFER * data)
{
	CLSV_BUFFER buffer;
	Tmsg msg;
	sessionInfoT * session;
	int flag=1;

	ToBegin(_info.list);

	while(flag && (session = GetDato(_info.list)) != NULL)
	{
		if(session->session == data->data.clsv_listu.session)
		{
			memcpy(&(buffer.data), &(data->data), sizeof(DATA));
			buffer.opcode = LISTU;
			flag = 0;
		}
	}

	if(!flag)
		msg.mtype = session->pid;
	else
	{
		buffer.data.svcl_listu.status = ERROR_SESS_NOT_EXISTS;
		msg.mtype = data->data.clsv_listu.upid;
	}

	memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
	myMsgsnd(&msg, TIMEOUT, _info.mqid);

	return;
}

void
process_disc(CLSV_BUFFER * data)
{
	sessionInfoT session;

	session.session = data->data.clsv_disc.session;

	listElemDelete(_info.list, &session);

	return;
}

void
process_info(CLSV_BUFFER * info)
{
	Tmsg msg;
	sessionInfoT session;
	CLSV_BUFFER buffer;

	if(info->data.clsv_info.status == NEW_SERVER)
	{
		session.pid = info->data.clsv_info.spid;
		session.session =  info->data.clsv_info.session;
		if(!listInsert(_info.list, &session))
		{
			buffer.opcode = INFO;
			buffer.data.svcl_info.status = ERROR_CONN;
		}
		else
		{
			buffer.opcode = INFO;
			buffer.data.svcl_info.status = ERROR_CONN;
		}
		msg.mtype = info->data.clsv_info.upid;
		memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
		myMsgsnd(&msg, TIMEOUT, _info.mqid);
	}

}


int
chCompare(listElementT element1, listElementT element2)
{
	return strcmp(((cliInfoT*)element1)->nick, ((cliInfoT*)element2)->nick);
}

listElementT
chCopy(listElementT element)
{
	cliInfoT *resp, *aux;

	resp = malloc(sizeof(cliInfoT));
	if(resp == NULL)
	{
		myError(ERROR_MALLOC);
		return NULL;
	}

	aux = (cliInfoT *) element;
	resp->upid = aux->upid;
	strcpy(resp->nick, aux->nick);
	return resp;
}

void
chFree(listElementT element)
{
	free(element);
}


void
process_newUser(CLSV_BUFFER * data)
{
	CLSV_BUFFER buffer;
	cliInfoT client;
	Tmsg msg;

	strcpy(client.nick, data->data.clsv_conn.nick);
	buffer.opcode = CONN;

	if(listElemBelong(_info.list, &client))
	{
		buffer.data.svcl_conn.status = ERROR_NICK_EXISTS;
		msg.mtype = data->data.clsv_conn.upid;
		memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
	}
	else
	{
		client.upid = data->data.clsv_conn.upid;

		if(!listInsert(_info.list, &client))
			buffer.data.svcl_conn.status = ERROR_ADDING_USER;
		else
		{
			buffer.data.svcl_conn.status = OK_;
			buffer.data.svcl_conn.spid = getpid();
			_info.cantelem++;
		}
		msg.mtype = data->data.clsv_conn.upid;
		memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
	}
	myMsgsnd(&msg, TIMEOUT, _info.mqid);

}

void
informSessionsServer(CLSV_BUFFER * data, CLSV_BUFFER * buffer)
{
	Tmsg msg;
	CLSV_BUFFER info;
	int error = 0;
	int pid;

	pid = getpid();
	info.opcode = INFO;
	info.data.clsv_info.spid = pid;
	info.data.clsv_info.status = NEW_SERVER;
	info.data.clsv_info.session = data->data.clsv_conn.session;
	msg.mtype = 1;
	memcpy(msg.mtext, &info, sizeof(CLSV_BUFFER));
	if(myMsgsnd(&msg, TIMEOUT, _info.mqid) == -1)
		error = 1;
	else
		 if(myMsgrcv(&msg, TIMEOUT, pid, _info.mqid) == -1)
		 	error = 1;
	memcpy(&info, msg.mtext, sizeof(CLSV_BUFFER));

	if(error || info.data.svcl_info.status == ERROR_CONN)
	{
		buffer->opcode = CONN;
		buffer->data.svcl_conn.status = ERROR_CONN;
		buffer->data.svcl_conn.spid = -1;
	}
}

void
convertToServer(CLSV_BUFFER * buffer, CLSV_BUFFER * data)
{
	CLSV_BUFFER info;
	Tmsg msg;
	cliInfoT client;
	int error=0;

	if((_info.list = listNew(chCompare, chCopy, chFree)) == NULL)
	{
		buffer->opcode = CONN;
		buffer->data.svcl_conn.status = ERROR_MALLOC;
		buffer->data.svcl_conn.spid = -1;
		error = 1;
	}
	else
	{
		strcpy(client.nick, data->data.clsv_conn.nick);
		client.upid = getpid();
		if(listInsert(_info.list, &client) != 1)
		{
			buffer->opcode = CONN;
			buffer->data.svcl_conn.status = ERROR_LIST;
			buffer->data.svcl_conn.spid = -1;
			error = 1;
		}
		else
		{
			buffer->opcode = CONN;
			buffer->data.svcl_conn.status = OK_;
			buffer->data.svcl_conn.spid = 1;
			_info.spid = getpid();
			_info.session = data->data.clsv_conn.session;
			_info.cantelem++;
			_info.cpid = data->data.clsv_conn.upid;
		}
	}

	if(!error)
		informSessionsServer(data, buffer);

}


void
process_chConn(CLSV_BUFFER * data)
{
	Tmsg msg;
	CLSV_BUFFER buffer;
	cliInfoT client;
	int pid, error = 0, cpid;

	if(data == NULL)
		return;

	pid = getpid();
	cpid = data->data.clsv_conn.upid;

	if(_info.spid == pid) //if this is the session server
	{
		process_newUser(data);
		return;
	}

 	//pid to which the sessions server must respond
	data->data.clsv_conn.upid = pid;

	msg.mtype = 1;
	memcpy(msg.mtext, data, sizeof(CLSV_BUFFER));

	/* send to the sessions server the client's message to see who is the 
	 * server for the required session */
	if(myMsgsnd(&msg, TIMEOUT, _info.mqid) == -1)
	{
		error = 1;
		buffer.opcode = CONN;
		buffer.data.svcl_conn.status = ERROR_SERVER;
		buffer.data.svcl_conn.spid = -1;
	}
	buffer.opcode = CONN;
	/* receiving the answer from the sessions server*/
	if(!error && myMsgrcv(&msg, 0, pid, _info.mqid) != -1)
	{
		if(errno != EINTR)
			memcpy(&buffer, msg.mtext, sizeof(CLSV_BUFFER));
		else
			error = 1;

	}
	else
	{
		error = 1;
		buffer.data.svcl_conn.status = ERROR_SERVER;
		buffer.data.svcl_conn.spid = -1;
	}

	if(!error && buffer.opcode != CONN) //if the answer is not valid
	{
		buffer.data.svcl_conn.status = INCON_OPC;
		buffer.data.svcl_conn.spid = -1;
	}
	else
	{
		if(!error)
		{/* if there is no server for the requested session this process
		  * becomes the server for this session*/
			if(buffer.data.svcl_conn.status == NO_SERVER)
			{
				data->data.clsv_conn.upid = cpid;
				convertToServer(&buffer, data);
			}
			else
			{
				_info.spid = buffer.data.svcl_conn.spid;
				_info.session = data->data.clsv_conn.session;
			}

		}
		else
			buffer.data.svcl_conn.status = ERROR_SERVER;
	}

	sendAns( srvData.cliSess, &buffer, sizeof(CLSV_BUFFER));
	if(buffer.data.svcl_conn.status == OK_)
		if((_info.mqid2 = msgget(MSG_KEY2, 0)) == -1)
		{
			perror("Error: Can't access message queue2");
			return;
		}

	return;
}


void
process_chLists(CLSV_BUFFER * data)
{
	CLSV_BUFFER answer;
	Tmsg msg;
	int error = 0, pid;

	pid = getpid();


	msg.mtype = 1;
	data->data.clsv_lists.upid = pid;
	memcpy(msg.mtext, data, sizeof(CLSV_BUFFER));

	if(myMsgsnd(&msg, 0, _info.mqid) == -1)
	{
		answer.opcode = LISTS;
		answer.data.svcl_lists.status = ERROR_LISTS;
		error = 1;
	}


	if(!error)
	{
		if(myMsgrcv(&msg, TIMEOUT, pid, _info.mqid) != -1)
			memcpy(&answer, msg.mtext, sizeof(CLSV_BUFFER));
		else
		{
			answer.opcode = LISTS;
			answer.data.svcl_lists.status = ERROR_LISTS;
		}
	}
	sendAns( srvData.cliSess, &answer, sizeof(CLSV_BUFFER));

}

int
GetSessionClients(CLSV_BUFFER * buffer)
{
	cliInfoT * client;
	int i=0;

	if(buffer != NULL)
	{
		ToBegin(_info.list);

		while((client = GetDato(_info.list)) != NULL)
		{
			i += sprintf((buffer->data.svcl_listu.list)+i, "%s\n", client->nick);

		}
		(buffer->data.svcl_listu.list)[i] = 0;
	}
	else
		return -1;

	if(i == 0)
		buffer->data.svcl_listu.status = ERROR_NO_USERS;
	else
		buffer->data.svcl_listu.status = OK_;

	buffer->opcode = LISTU;

	return 1;

}

void
process_chListu(CLSV_BUFFER * data)
{
	Tmsg msg;
	CLSV_BUFFER buffer;
	sessionInfoT session;
	int i=0, error = 0, pid;

	if(data == NULL)
		return;

	pid = getpid();

	/* if this process is the server for the requested session */
	if(_info.spid == pid && _info.session == data->data.clsv_listu.session )
	{
		buffer.opcode = LISTU;
		if(GetSessionClients(&buffer) == -1)
		{
			buffer.data.svcl_listu.status = ERROR_SERVER;
			buffer.data.svcl_listu.spid = -1;
		}
		if(data->data.clsv_listu.upid != -1)
		{
			msg.mtype = data->data.clsv_listu.upid;
			memcpy(msg.mtext, &buffer, sizeof(CLSV_BUFFER));
			myMsgsnd(&msg, TIMEOUT, _info.mqid);
			return;
		}
	}
	else
	{
		msg.mtype = 1;
		data->data.clsv_listu.upid = getpid();
		memcpy(msg.mtext, data, sizeof(CLSV_BUFFER));

		if(myMsgsnd(&msg, TIMEOUT, _info.mqid) == -1)
		{
			error = 1;
			buffer.opcode = LISTU;
			buffer.data.svcl_listu.status = ERROR_SERVER;
			buffer.data.svcl_listu.spid = -1;
		}

		if(!error && myMsgrcv(&msg, TIMEOUT, pid, _info.mqid) == -1)
		{
			error = 1;
			buffer.opcode = LISTU;
			buffer.data.svcl_listu.status = ERROR_SERVER;
			buffer.data.svcl_listu.spid = -1;
		}

		if(!error)
		{
			memcpy(&buffer, msg.mtext, sizeof(CLSV_BUFFER));

		}

	}
	sendAns( srvData.cliSess, &buffer, sizeof(CLSV_BUFFER));
	return;
}

void
process_chMessR(CLSV_BUFFER * data)
{
	Tmsg msg;
	cliInfoT * client;
	CLSV_BUFFER buffer;
	int error = 0;

	buffer.opcode = CHATS;
	buffer.data.svcl_chat.status = OK_;
	strcpy(buffer.data.svcl_chat.msg, data->data.clsv_chat.msg);
	strcpy(buffer.data.svcl_chat.nick, data->data.clsv_chat.nick);

	if(_info.spid == getpid())
	{
		ToBegin(_info.list);
		while(!error && (client = GetDato(_info.list)) != NULL)
		{
			/* send the message to all clients in the session */
			msg.mtype = client->upid;
			memcpy(&(msg.mtext), &buffer, sizeof(CLSV_BUFFER));

			if(myMsgsnd(&msg, TIMEOUT, _info.mqid2) == -1)
				error = 1;
		}
	}
	else if(_info.spid > 0 )
	{
		buffer.opcode = CHATR;
		msg.mtype = _info.spid;
		memcpy(&(msg.mtext), &buffer, sizeof(CLSV_BUFFER));

		if(myMsgsnd(&msg, TIMEOUT, _info.mqid2) == -1)
			error = 1;
	}
	else
		error = 1;

	if(error)
	{
		buffer.opcode = CHATR;
		buffer.data.svcl_chat.status = ERROR_CHAT;
		sendAns( srvData.cliSess, &buffer, sizeof(CLSV_BUFFER));
	}

}

void
process_chMessS(CLSV_BUFFER * data)
{
	sendAns( srvData.cliSess, data, sizeof(CLSV_BUFFER));

}

void
process_chDisc(CLSV_BUFFER * data)
{
	CLSV_BUFFER buffer;
	cliInfoT client;
	Tmsg msg;

	if(_info.spid == getpid())// if the process is the session server
	{
		strcpy(client.nick, data->data.clsv_disc.nick);

		listElemDelete (_info.list, &client);
		_info.cantelem--;


		if(_info.cpid == data->data.clsv_disc.upid)
		{
			if( closeSess(srvData.cliSess) != ERROR_OK )
				fprintf(stderr, "Error closing session.\n");
			srvData.cliSess= NULL;
			_info.cpid = -1;
		}

		/* if all the clients in the session leave, end the session */
		if(_info.cantelem==0)
		{
			msg.mtype = 1;
			memcpy(msg.mtext, data, sizeof(CLSV_BUFFER));
			myMsgsnd(&msg, TIMEOUT, _info.mqid);
			_info.session = -1;
			listFree(_info.list);
		}

	}
	else
	{
		msg.mtype = _info.spid;
		memcpy(msg.mtext, data, sizeof(CLSV_BUFFER));
		myMsgsnd(&msg, TIMEOUT, _info.mqid);
		_info.spid = -1;
		_info.session = -1;
	}
}

